/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. 

  Keepass2Android is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Keepass2Android is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.
  */


using System;
using System.Linq;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Content.PM;
using Android.Preferences;
using keepass2android;
using KeePassLib;
using KeePassLib.Utility;

namespace keepass2android
{
  [Activity(Label = "@string/kp2a_findUrl", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden, Theme = "@style/Kp2aTheme_ActionBar", Exported = true)]
#if NoNet
    [MetaData("android.app.searchable", Resource = "@xml/searchable_offline")]
#else
#if DEBUG
  [MetaData("android.app.searchable", Resource = "@xml/searchable_debug")]
#else
    [MetaData("android.app.searchable", Resource = "@xml/searchable")]
#endif
#endif
  [MetaData("android.app.default_searchable", Value = "keepass2android.search.SearchResults")]
  [IntentFilter(new[] { Intent.ActionSearch }, Categories = new[] { Intent.CategoryDefault })]
  public class ShareUrlResults : GroupBaseActivity
  {

    public ShareUrlResults(IntPtr javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {

    }

    public ShareUrlResults()
    {
    }

    public static void Launch(Activity act, SearchUrlTask task, ActivityLaunchMode launchMode)
    {
      Intent i = new Intent(act, typeof(ShareUrlResults));
      task.ToIntent(i);
      launchMode.Launch(act, i);
    }

    public static void Launch(Activity act, OpenSpecificEntryTask task, ActivityLaunchMode launchMode)
    {
      Intent i = new Intent(act, typeof(ShareUrlResults));
      task.ToIntent(i);
      launchMode.Launch(act, i);
    }

    public override bool IsSearchResult
    {
      get { return true; }
    }

    protected override void OnCreate(Bundle savedInstanceState)
    {
      //we don't want any background thread to update/reload the database while we're in this activity.
      if (!App.Kp2a.DatabasesBackgroundModificationLock.TryEnterReadLock(TimeSpan.FromSeconds(5)))
      {
        App.Kp2a.ShowMessage(this, GetString(Resource.String.failed_to_access_database), MessageSeverity.Error);
        Finish();
        return;
      }

      base.OnCreate(savedInstanceState);

      //if user presses back to leave this activity:
      SetResult(Result.Canceled);

      UpdateBottomBarElementVisibility(Resource.Id.select_other_entry, true);
      UpdateBottomBarElementVisibility(Resource.Id.add_url_entry, true);

      if (App.Kp2a.DatabaseIsUnlocked)
      {
        Query();
      }
      // else: LockCloseListActivity.OnResume will trigger a broadcast (LockDatabase) which will cause the activity to be finished.

    }

    protected override void OnSaveInstanceState(Bundle outState)
    {
      base.OnSaveInstanceState(outState);
      AppTask.ToBundle(outState);
    }

    private void Query()
    {
      bool canAutoReturnFromQuery = true;
      bool shouldAutoReturnFromQuery = true;
      try
      {
        if (AppTask is SearchUrlTask searchUrlTask)
        {
          String searchUrl = searchUrlTask.UrlToSearchFor;
          canAutoReturnFromQuery = searchUrlTask.AutoReturnFromQuery;
          shouldAutoReturnFromQuery = PreferenceManager.GetDefaultSharedPreferences(this)
              .GetBoolean(GetString(Resource.String.AutoReturnFromQuery_key), true);
          Group = GetSearchResultsForUrl(searchUrl);
        }
        else if (AppTask is OpenSpecificEntryTask openEntryTask)
        {
          Group = GetSearchResultsForUuid(openEntryTask.EntryUuid);
        }

      }
      catch (Exception e)
      {
        App.Kp2a.ShowMessage(this, Util.GetErrorMessage(e), MessageSeverity.Error);
        SetResult(Result.Canceled);
        Finish();
        return;
      }

      //if there is exactly one match: open the entry
      if ((Group.Entries.Count() == 1) && canAutoReturnFromQuery && shouldAutoReturnFromQuery)
      {
        LaunchActivityForEntry(Group.Entries.Single(), 0);
        return;
      }

      //show results:
      if (Group == null || (!Group.Entries.Any()))
      {
        SetContentView(Resource.Layout.searchurlresults_empty);
      }

      SetGroupTitle();

      FragmentManager.FindFragmentById<GroupListFragment>(Resource.Id.list_fragment).ListAdapter = new PwGroupListAdapter(this, Group);

      View selectOtherEntry = FindViewById(Resource.Id.select_other_entry);
      View createUrlEntry = FindViewById(Resource.Id.add_url_entry);

      if (AppTask is OpenSpecificEntryTask)
      {
        selectOtherEntry.Visibility = ViewStates.Gone;
        createUrlEntry.Visibility = ViewStates.Gone;
      }
      else
      {
        var searchUrlTask = AppTask as SearchUrlTask;
        String searchUrl = searchUrlTask.UrlToSearchFor;
        selectOtherEntry.Visibility = ViewStates.Visible;

        SearchUrlTask newTask;
        if (AppTask is SelectEntryTask currentSelectTask)
        {
          newTask = new SearchUrlTask() { AutoReturnFromQuery = false, UrlToSearchFor = searchUrl, ActivateKeyboard = currentSelectTask.ActivateKeyboard };
          newTask.ShowUserNotifications = currentSelectTask.ShowUserNotifications;
          newTask.ActivateKeyboard = currentSelectTask.ActivateKeyboard;
          newTask.CopyTotpToClipboard = currentSelectTask.CopyTotpToClipboard;
        }
        else
          newTask = new SearchUrlTask() { AutoReturnFromQuery = false, UrlToSearchFor = searchUrl, ActivateKeyboard = ActivationCondition.Never };


        selectOtherEntry.Click += (sender, e) =>
        {
          GroupActivity.Launch(this, newTask, new ActivityLaunchModeRequestCode(0));

        };




        if (App.Kp2a.OpenDatabases.Any(db => db.CanWrite))
        {
          createUrlEntry.Visibility = ViewStates.Visible;
          createUrlEntry.Click += (sender, e) =>
          {
            GroupActivity.Launch(this, new CreateEntryThenCloseTask { Url = searchUrl, ShowUserNotifications = (AppTask as SelectEntryTask)?.ShowUserNotifications ?? ActivationCondition.Always }, new ActivityLaunchModeRequestCode(0));
            App.Kp2a.ShowMessage(this, GetString(Resource.String.select_group_then_add, new Java.Lang.Object[] { GetString(Resource.String.add_entry) }), MessageSeverity.Info);
          };
        }
        else
        {
          createUrlEntry.Visibility = ViewStates.Gone;
        }

      }




      Util.MoveBottomBarButtons(Resource.Id.select_other_entry, Resource.Id.add_url_entry, Resource.Id.bottom_bar, this);
    }

    public static PwGroup GetSearchResultsForUrl(string url)
    {
      PwGroup resultsGroup = null;
      foreach (var db in App.Kp2a.OpenDatabases)
      {
        //first: search for exact url
        var resultsForThisDb = db.SearchForExactUrl(url);
        if (!url.StartsWith(KeePass.AndroidAppScheme))
        {
          //if no results, search for host (e.g. "accounts.google.com")
          if (!resultsForThisDb.Entries.Any())
            resultsForThisDb = db.SearchForHost(url, false);
          //if still no results, search for host, allowing subdomains ("www.google.com" in entry is ok for "accounts.google.com" in search (but not the other way around)
          if (!resultsForThisDb.Entries.Any())
            resultsForThisDb = db.SearchForHost(url, true);
        }

        //if no results returned up to now, try to search through other fields as well:
        if (!resultsForThisDb.Entries.Any())
          resultsForThisDb = db.SearchForText(url);
        //search for host as text
        if (!resultsForThisDb.Entries.Any())
          resultsForThisDb = db.SearchForText(UrlUtil.GetHost(url.Trim()));

        if (resultsGroup == null)
        {
          resultsGroup = resultsForThisDb;
        }
        else
        {
          foreach (var entry in resultsForThisDb.Entries)
          {
            resultsGroup.AddEntry(entry, false, false);
          }
        }
      }

      return resultsGroup;
    }


    public static PwGroup GetSearchResultsForUuid(string uuid)
    {
      PwGroup resultsGroup = null;
      foreach (var db in App.Kp2a.OpenDatabases)
      {

        var resultsForThisDb = db.SearchForUuid(uuid);

        if (resultsGroup == null)
        {
          resultsGroup = resultsForThisDb;
        }
        else
        {
          foreach (var entry in resultsForThisDb.Entries)
          {
            resultsGroup.AddEntry(entry, false, false);
          }
        }
      }

      return resultsGroup;
    }

    public override bool OnSearchRequested()
    {
      Intent i = new Intent(this, typeof(SearchActivity));
      this.AppTask.ToIntent(i);
      i.SetFlags(ActivityFlags.ForwardResult);
      StartActivity(i);
      return true;
    }

    protected override int ContentResourceId
    {
      get { return Resource.Layout.searchurlresults; }
    }

    public override bool EntriesBelongToCurrentDatabaseOnly
    {
      get { return false; }
    }

    public override ElementAndDatabaseId FullGroupId
    {
      get { return null; }
    }

    protected override void OnDestroy()
    {
      base.OnDestroy();
      App.Kp2a.DatabasesBackgroundModificationLock.ExitReadLock();
    }
  }
}

